package com.googlecode.mapperdao.drivers

import com.googlecode.mapperdao._
import com.googlecode.mapperdao.jdbc.{Batch, Jdbc}
import com.googlecode.mapperdao.queries.v2.QueryInfo
import com.googlecode.mapperdao.schema.{Column, ColumnBase, PK, SimpleColumn}
import com.googlecode.mapperdao.sqlbuilder.{SqlBuilder, SqlSelectBuilder}

/**
 * @author kostantinos.kougios
 *
 *         23 Sep 2011
 */
class Oracle(val jdbc: Jdbc, val typeRegistry: TypeRegistry, val typeManager: TypeManager) extends Driver
{

	def batchStrategy(autogenerated: Boolean) = if (autogenerated) Batch.NoBatch else Batch.WithBatch

	val escapeNamesStrategy = new EscapeNamesStrategy
	{
		val invalidColumnNames = Set("select", "where", "group", "start", "long", "float", "double")
		val invalidTableNames = Set("end", "select", "where", "group", "user")

		override def escapeColumnNames(name: String) = if (invalidColumnNames.contains(name.toLowerCase)) '"' + name + '"'; else name

		override def escapeTableNames(name: String) = if (invalidTableNames.contains(name.toLowerCase)) '"' + name + '"'; else name
	}
	val sqlBuilder = new SqlBuilder(this, escapeNamesStrategy)

	override protected[mapperdao] def getAutoGenerated(
		m: java.util.Map[String, Object],
		column: SimpleColumn
		) = m.get(column.name.toUpperCase)

	override protected def sequenceSelectNextSql(sequenceColumn: ColumnBase): String = sequenceColumn match {
		case PK(_, columnName, true, sequence, _) => "%s.nextval".format(sequence.get)
	}

	override def beforeStartOfQuery[ID, PC <: Persisted, T](q: SqlSelectBuilder, queryConfig: QueryConfig, qe: QueryInfo[ID, T], columns: List[SimpleColumn]) =
		if (queryConfig.offset.isDefined || queryConfig.limit.isDefined) {
			val nq = sqlBuilder.sqlSelectBuilder
			nq.columnNames(null, List("*"))

			val iq = sqlBuilder.sqlSelectBuilder
			nq.from(iq)

			iq.columnNames(null, "rownum as rn$" :: columns.map(_.name))
			iq.from(q)
			nq
		} else q

	private val rn = Column(null, "rn$", classOf[Long])
	private val rownum = Column(null, "rownum", classOf[Long])

	override def endOfQuery[ID, PC <: Persisted, T](q: SqlSelectBuilder, queryConfig: QueryConfig, qe: QueryInfo[ID, T]) =
		if (queryConfig.offset.isDefined || queryConfig.limit.isDefined) {
			val offset = queryConfig.offset.getOrElse(0l)

			q.where(null, rn, ">", offset)
			q.from match {
				case iq: SqlSelectBuilder =>
					iq.where(null, rownum, "<=", (if (queryConfig.limit.isDefined) queryConfig.limit.get + offset else Long.MaxValue))
			}
			q
			//			sql append "\n) where rownum<=" append (if (queryConfig.limit.isDefined) queryConfig.limit.get + offset else Long.MaxValue)
			//			sql append "\n) where rn$>" append offset
		} else q

	override def toString = "Oracle"
}